use crate::{AsJValue, AsJValueArray, JEnv, JObject, JReturnMaker};

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct JClassID {
	inner: sys::jclass,
}
impl JClassID {
	pub fn as_sys(&self) -> sys::jclass {
		self.inner
	}
	pub fn from_sys(value: sys::jclass) -> Option<Self> {
		if value.is_null() {
			None
		} else {
			Some(Self { inner: value })
		}
	}
	pub fn class(env: Option<&JEnv>) -> Option<JClassID> {
		const NAME: &'static [u8] = b"java/lang/Class\0";
		static CACHE: CachedID = CachedID::new();
		CACHE.get(|| JEnv::env(env)?.find_class(NAME))
	}
	pub fn as_object(&self) -> &JObject {
		unsafe { &*(self.inner as *const JObject) }
	}
	pub fn supper(&self, env: &JEnv) -> Option<JClassID> {
		env.super_class(*self)
	}
	pub fn method(
		&self,
		env: &JEnv, //
		name: &[u8],
		sign: &[u8],
	) -> Option<JMethodID> {
		env.method(*self, name, sign)
	}
	pub fn method_static(&self, env: &JEnv, name: &[u8], sign: &[u8]) -> Option<JMethodStaticID> {
		env.method_static(*self, name, sign)
	}
	pub fn field(&self, env: &JEnv, name: &[u8], sign: &[u8]) -> Option<JFieldID> {
		env.field(*self, name, sign)
	}
	pub fn field_static(&self, env: &JEnv, name: &[u8], sign: &[u8]) -> Option<JFieldStaticID> {
		env.field_static(*self, name, sign)
	}
}

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct JMethodID {
	inner: sys::jmethodID,
}
impl JMethodID {
	pub fn as_sys(&self) -> sys::jmethodID {
		self.inner
	}
	pub fn from_sys(id: sys::jmethodID) -> Option<Self> {
		if id.is_null() {
			None
		} else {
			Some(Self { inner: id })
		}
	}
	pub fn call<const N: usize, T: JReturnMaker>(
		&self,
		env: &JEnv, //
		target: &JObject,
		args: impl AsJValueArray<N>,
	) -> Option<T> {
		env.call_virtual(target, *self, args)
	}
	pub fn call_non_virtual<const N: usize, T: JReturnMaker>(
		&self,
		env: &JEnv,
		target: &JObject,
		class: JClassID,
		args: impl AsJValueArray<N>,
	) -> Option<T> {
		env.call_non_virtual(target, class, *self, args)
	}
}

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct JMethodStaticID {
	inner: sys::jmethodID,
}
impl JMethodStaticID {
	pub fn as_sys(&self) -> sys::jmethodID {
		self.inner
	}
	pub fn from_sys(id: sys::jmethodID) -> Option<Self> {
		if id.is_null() {
			None
		} else {
			Some(Self { inner: id })
		}
	}
	pub fn call<const N: usize, T: JReturnMaker>(
		&self,
		env: &JEnv, //
		class: JClassID,
		args: impl AsJValueArray<N>,
	) -> Option<T> {
		env.call_static(class, *self, args)
	}
}

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct JFieldID {
	inner: sys::jfieldID,
}
impl JFieldID {
	pub fn as_sys(&self) -> sys::jfieldID {
		self.inner
	}
	pub fn from_sys(id: sys::jfieldID) -> Option<Self> {
		if id.is_null() {
			None
		} else {
			Some(Self { inner: id })
		}
	}
	pub fn get<T: JReturnMaker>(
		&self,
		env: &JEnv, //
		object: &JObject,
	) -> Option<T> {
		env.get_field(object, *self)
	}
	pub fn set<T: JReturnMaker + AsJValue>(
		&self,
		env: &JEnv, //
		object: &JObject,
		value: T,
	) -> Option<()> {
		env.set_field(object, *self, value)
	}
}

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct JFieldStaticID {
	inner: sys::jfieldID,
}
impl JFieldStaticID {
	pub fn as_sys(&self) -> sys::jfieldID {
		self.inner
	}
	pub fn from_sys(id: sys::jfieldID) -> Option<Self> {
		if id.is_null() {
			None
		} else {
			Some(Self { inner: id })
		}
	}
	pub fn get<T: JReturnMaker>(
		&self,
		env: &JEnv, //
		class: JClassID,
	) -> Option<T> {
		env.get_static(class, *self)
	}
	pub fn set<T: JReturnMaker + AsJValue>(
		&self,
		env: &JEnv, //
		class: JClassID,
		value: T,
	) -> Option<()> {
		env.set_static(class, *self, value)
	}
}

mod cache {
	use core::{ffi, ptr};
	use std::sync::atomic::{AtomicPtr, Ordering};

	pub trait CachableID: Sized {
		fn from_void(v: *mut ffi::c_void) -> Option<Self>;
		fn to_void(&self) -> *mut ffi::c_void;
	}
	impl CachableID for super::JClassID {
		fn from_void(v: *mut ffi::c_void) -> Option<Self> {
			Self::from_sys(v as sys::jclass)
		}
		fn to_void(&self) -> *mut ffi::c_void {
			self.as_sys() as _
		}
	}
	impl CachableID for super::JFieldID {
		fn from_void(v: *mut ffi::c_void) -> Option<Self> {
			Self::from_sys(v as sys::jfieldID)
		}
		fn to_void(&self) -> *mut ffi::c_void {
			self.as_sys() as _
		}
	}
	impl CachableID for super::JFieldStaticID {
		fn from_void(v: *mut ffi::c_void) -> Option<Self> {
			Self::from_sys(v as sys::jfieldID)
		}
		fn to_void(&self) -> *mut ffi::c_void {
			self.as_sys() as _
		}
	}
	impl CachableID for super::JMethodID {
		fn from_void(v: *mut ffi::c_void) -> Option<Self> {
			Self::from_sys(v as sys::jmethodID)
		}
		fn to_void(&self) -> *mut ffi::c_void {
			self.as_sys() as _
		}
	}
	impl CachableID for super::JMethodStaticID {
		fn from_void(v: *mut ffi::c_void) -> Option<Self> {
			Self::from_sys(v as sys::jmethodID)
		}
		fn to_void(&self) -> *mut ffi::c_void {
			self.as_sys() as _
		}
	}
	pub struct CachedID {
		value: AtomicPtr<ffi::c_void>,
	}
	impl CachedID {
		pub const fn new() -> Self {
			Self {
				value: AtomicPtr::new(ptr::null_mut()),
			}
		}
		pub fn get<T: CachableID>(&self, fun: impl Fn() -> Option<T>) -> Option<T> {
			let ptr = self.value.load(Ordering::Relaxed);
			if ptr.is_null() {
				if let Some(v) = fun() {
					self.value.store(v.to_void(), Ordering::Relaxed);
					Some(v)
				} else {
					None
				}
			} else {
				T::from_void(ptr)
			}
		}
	}

	// #[test]
	// fn test_cache() {
	// 	impl CachableID for usize {
	// 		fn from_void(v: *mut ffi::c_void) -> Option<Self> {
	// 			Some(v as usize)
	// 		}
	// 		fn to_void(&self) -> *mut ffi::c_void {
	// 			*self as _
	// 		}
	// 	}
	// 	static C: CachedID = CachedID::new();
	// 	assert_eq!(Some(100), C.get(|| Some(100)));
	// 	assert_eq!(Some(100), C.get(|| Some(200)));
	// 	assert_eq!(Some(100), C.get(|| Some(300)));
	// }
}
pub use cache::*;

// mod class_id {
// 	use crate::*;

// 	impl JClassID {
// 		pub fn for_name<'a>(env: &'a JEnv, arg0: &'a JString) -> Option<JClassID> {
// 			let class = Self::class(Some(env))?;
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					class.method_static(
// 						env,
// 						"forName",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/String")), JSignRef("java/lang/Class")),
// 					)
// 				})?
// 				.call(env, class, (arg0.as_sys(),))
// 		}

// 		pub fn for_name_v2<'a>(env: &'a JEnv, arg0: &'a JString, arg1: JBool, arg2: &'a JObject) -> Option<JClassID> {
// 			let class = Self::class(Some(env))?;
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					class.method_static(
// 						env,
// 						"forName",
// 						&JSignFun(
// 							JSignArg3(JSignRef("java/lang/String"), JSignBool, JSignRef("java/lang/ClassLoader")),
// 							JSignRef("java/lang/Class"),
// 						),
// 					)
// 				})?
// 				.call(env, class, (arg0.as_sys(), arg1, arg2.as_sys()))
// 		}

// 		pub fn to_string<'a>(&'a self, env: &'a JEnv) -> Option<&'a JString> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "toString", &JSignFun(JSignArg0, JSignRef("java/lang/String"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn to_generic_string<'a>(&'a self, env: &'a JEnv) -> Option<&'a JString> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "toGenericString", &JSignFun(JSignArg0, JSignRef("java/lang/String"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn new_instance<'a>(&'a self, env: &'a JEnv) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "newInstance", &JSignFun(JSignArg0, JSignRef("java/lang/Object"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_instance<'a>(&'a self, env: &'a JEnv, arg0: &'a JObject) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isInstance", &JSignFun(JSignArg1(JSignRef("java/lang/Object")), JSignBool)))?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn is_assignable_from<'a>(&'a self, env: &'a JEnv, arg0: JClassID) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"isAssignableFrom",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/Class")), JSignBool),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn is_interface<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isInterface", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_array<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isArray", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_primitive<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isPrimitive", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_annotation<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isAnnotation", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_synthetic<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isSynthetic", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_name<'a>(&'a self, env: &'a JEnv) -> Option<&'a JString> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getName", &JSignFun(JSignArg0, JSignRef("java/lang/String"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_class_loader<'a>(&'a self, env: &'a JEnv) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getClassLoader", &JSignFun(JSignArg0, JSignRef("java/lang/ClassLoader"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_type_parameters<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getTypeParameters",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/TypeVariable"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_superclass<'a>(&'a self, env: &'a JEnv) -> Option<JClassID> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getSuperclass", &JSignFun(JSignArg0, JSignRef("java/lang/Class"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_generic_superclass<'a>(&'a self, env: &'a JEnv) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getGenericSuperclass",
// 						&JSignFun(JSignArg0, JSignRef("java/lang/reflect/Type")),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_package<'a>(&'a self, env: &'a JEnv) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getPackage", &JSignFun(JSignArg0, JSignRef("java/lang/Package"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_package_name<'a>(&'a self, env: &'a JEnv) -> Option<&'a JString> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getPackageName", &JSignFun(JSignArg0, JSignRef("java/lang/String"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_interfaces<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(env, "getInterfaces", &JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/Class"))))
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_generic_interfaces<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getGenericInterfaces",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/Type"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_component_type<'a>(&'a self, env: &'a JEnv) -> Option<JClassID> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getComponentType", &JSignFun(JSignArg0, JSignRef("java/lang/Class"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_modifiers<'a>(&'a self, env: &'a JEnv) -> Option<JInt> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getModifiers", &JSignFun(JSignArg0, JSignInt)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_signers<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getSigners", &JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/Object")))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_enclosing_method<'a>(&'a self, env: &'a JEnv) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getEnclosingMethod",
// 						&JSignFun(JSignArg0, JSignRef("java/lang/reflect/Method")),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_enclosing_constructor<'a>(&'a self, env: &'a JEnv) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getEnclosingConstructor",
// 						&JSignFun(JSignArg0, JSignRef("java/lang/reflect/Constructor")),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_declaring_class<'a>(&'a self, env: &'a JEnv) -> Option<JClassID> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getDeclaringClass", &JSignFun(JSignArg0, JSignRef("java/lang/Class"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_enclosing_class<'a>(&'a self, env: &'a JEnv) -> Option<JClassID> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getEnclosingClass", &JSignFun(JSignArg0, JSignRef("java/lang/Class"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_simple_name<'a>(&'a self, env: &'a JEnv) -> Option<&'a JString> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getSimpleName", &JSignFun(JSignArg0, JSignRef("java/lang/String"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_type_name<'a>(&'a self, env: &'a JEnv) -> Option<&'a JString> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getTypeName", &JSignFun(JSignArg0, JSignRef("java/lang/String"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_canonical_name<'a>(&'a self, env: &'a JEnv) -> Option<&'a JString> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getCanonicalName", &JSignFun(JSignArg0, JSignRef("java/lang/String"))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_anonymous_class<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isAnonymousClass", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_local_class<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isLocalClass", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_member_class<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isMemberClass", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_classes<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "getClasses", &JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/Class")))))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_fields<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getFields",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/Field"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_methods<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getMethods",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/Method"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_constructors<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getConstructors",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/Constructor"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_field<'a>(&'a self, env: &'a JEnv, arg0: &'a JString) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getField",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/String")), JSignRef("java/lang/reflect/Field")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_method<'a>(&'a self, env: &'a JEnv, arg0: &'a JString, arg1: &'a JArrayObject<JObject>) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getMethod",
// 						&JSignFun(
// 							JSignArg2(JSignRef("java/lang/String"), JSignArray(JSignRef("java/lang/Class"))),
// 							JSignRef("java/lang/reflect/Method"),
// 						),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(), arg1.as_sys()))
// 		}

// 		pub fn get_constructor<'a>(&'a self, env: &'a JEnv, arg0: &'a JArrayObject<JObject>) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getConstructor",
// 						&JSignFun(
// 							JSignArg1(JSignArray(JSignRef("java/lang/Class"))),
// 							JSignRef("java/lang/reflect/Constructor"),
// 						),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_declared_classes<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredClasses",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/Class"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_declared_fields<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredFields",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/Field"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_declared_methods<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredMethods",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/Method"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_declared_constructors<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredConstructors",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/reflect/Constructor"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_declared_field<'a>(&'a self, env: &'a JEnv, arg0: &'a JString) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredField",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/String")), JSignRef("java/lang/reflect/Field")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_declared_method<'a>(&'a self, env: &'a JEnv, arg0: &'a JString, arg1: &'a JArrayObject<JObject>) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredMethod",
// 						&JSignFun(
// 							JSignArg2(JSignRef("java/lang/String"), JSignArray(JSignRef("java/lang/Class"))),
// 							JSignRef("java/lang/reflect/Method"),
// 						),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(), arg1.as_sys()))
// 		}

// 		pub fn get_declared_constructor<'a>(&'a self, env: &'a JEnv, arg0: &'a JArrayObject<JObject>) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredConstructor",
// 						&JSignFun(
// 							JSignArg1(JSignArray(JSignRef("java/lang/Class"))),
// 							JSignRef("java/lang/reflect/Constructor"),
// 						),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_resource_as_stream<'a>(&'a self, env: &'a JEnv, arg0: &'a JString) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getResourceAsStream",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/String")), JSignRef("java/io/InputStream")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_resource<'a>(&'a self, env: &'a JEnv, arg0: &'a JString) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getResource",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/String")), JSignRef("java/net/URL")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_protection_domain<'a>(&'a self, env: &'a JEnv) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getProtectionDomain",
// 						&JSignFun(JSignArg0, JSignRef("java/security/ProtectionDomain")),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn desired_assertion_status<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "desiredAssertionStatus", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn is_enum<'a>(&'a self, env: &'a JEnv) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| Self::class(Some(env))?.method(env, "isEnum", &JSignFun(JSignArg0, JSignBool)))?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_enum_constants<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getEnumConstants",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/Object"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn cast<'a>(&'a self, env: &'a JEnv, arg0: &'a JObject) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"cast",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/Object")), JSignRef("java/lang/Object")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn as_subclass<'a>(&'a self, env: &'a JEnv, arg0: JClassID) -> Option<JClassID> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"asSubclass",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/Class")), JSignRef("java/lang/Class")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_annotation<'a>(&'a self, env: &'a JEnv, arg0: JClassID) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getAnnotation",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/Class")), JSignRef("java/lang/annotation/Annotation")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn is_annotation_present<'a>(&'a self, env: &'a JEnv, arg0: JClassID) -> Option<JBool> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"isAnnotationPresent",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/Class")), JSignBool),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_annotations_by_type<'a>(&'a self, env: &'a JEnv, arg0: JClassID) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getAnnotationsByType",
// 						&JSignFun(
// 							JSignArg1(JSignRef("java/lang/Class")),
// 							JSignArray(JSignRef("java/lang/annotation/Annotation")),
// 						),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_annotations<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getAnnotations",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/annotation/Annotation"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}

// 		pub fn get_declared_annotation<'a>(&'a self, env: &'a JEnv, arg0: JClassID) -> Option<&'a JObject> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredAnnotation",
// 						&JSignFun(JSignArg1(JSignRef("java/lang/Class")), JSignRef("java/lang/annotation/Annotation")),
// 					)
// 				})?
// 				.call(env, self.as_object(), (arg0.as_sys(),))
// 		}

// 		pub fn get_declared_annotations<'a>(&'a self, env: &'a JEnv) -> Option<&'a JArrayObject<JObject>> {
// 			static CACHE: CachedID = CachedID::new();
// 			CACHE
// 				.get(|| {
// 					Self::class(Some(env))?.method(
// 						env,
// 						"getDeclaredAnnotations",
// 						&JSignFun(JSignArg0, JSignArray(JSignRef("java/lang/annotation/Annotation"))),
// 					)
// 				})?
// 				.call(env, self.as_object(), ())
// 		}
// 	}
// }
